﻿#region

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using Tateeda.ClinicalStudy.Base.Data.DBModel;
using Tateeda.ClinicalStudy.Base.Data.EF;
using Tateeda.ClinicalStudy.Base.Modules.Common;
using Tateeda.ClinicalStudy.Base.Modules.Common.Enumeration;
using Tateeda.ClinicalStudy.Base.Modules.Common.Extenstions;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Interfaces.Services;
using Tateeda.ClinicalStudy.Base.Modules.Models.StudyForm;

#endregion

#region - Legal -

///<legal>
/// Copyright © 2011, Tateeda Media Network
/// All rights reserved. http://tateeda.com
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions
/// are met:
///
/// - Redistributions of source code must retain the above copyright
/// notice, this list of conditions and the following disclaimer.
///
/// - Neither the name of the Tateeda Media Network, nor the names of its
/// contributors may be used to endorse or promote products
/// derived from this software without specific prior written
/// permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
/// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
/// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
/// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
/// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
/// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
/// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
/// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///</legal>

#endregion - Legal -

namespace Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Concrete.Services {

    public class FormBuilderService : IFormBuilder {
        private readonly IUnitOfWork _unitOfWork;

        public FormBuilderService() {
            _unitOfWork = new SqlUnitOfWork();
        }

        #region IFormBuilder Members

        public virtual IUnitOfWork UnitOfWork {
            get { return _unitOfWork; }
        }

        public virtual AnswerModel AddAnswer(AnswerModel answer) {
            Answer newAnswer = answer.ToAnswer();
            UnitOfWork.Answers.AddObject(newAnswer);
            UnitOfWork.Commit();
            answer = newAnswer.ToAnswerModel();
            return answer;
        }

        /// <summary>
        ///   Add new question.
        /// </summary>
        /// <param name = "question">The question.</param>
        /// <returns></returns>
        public virtual QuestionModel AddQuestion(QuestionModel question) {
            Question newQuestion = question.ToQuestion();
            UnitOfWork.Questions.AddObject(newQuestion);
            UnitOfWork.Commit();
            question = newQuestion.ToQuestionModel();
            return question;
        }

        public virtual StudyFormModel CreateNewForm(StudyFormModel form) {
            Form newForm = form.ToForm();
            UnitOfWork.Forms.AddObject(newForm);
            UnitOfWork.Commit();
            form = newForm.ToFormModel();
            return form;
        }

        public virtual void DeleteAnswer(int answerId) {
            Answer answer = UnitOfWork.Answers.Where(a => a.AnswerId == answerId).SingleOrDefault();
            UnitOfWork.Answers.DeleteObject(answer);
            UnitOfWork.Commit();
        }

        public virtual void DeleteForm(int formId) {
            Form delForm = UnitOfWork.Forms.Where(form => form.FormId == formId).SingleOrDefault();

            if (delForm != null) {
                ICollection<int> questinsIDs = delForm.Questions.Select(q => q.QuestionId).ToList();
                foreach (int q in questinsIDs) {
                    DeleteQuestion(q);
                }
            }

            UnitOfWork.Forms.DeleteObject(delForm);
            UnitOfWork.Commit();
        }

        public virtual void DeleteQuestion(int questionId) {
            Question question = UnitOfWork.Questions.Where(q => q.QuestionId == questionId).SingleOrDefault();

            if (question != null) {
                ICollection<int> answerIDs = question.Answers.Select(an => an.AnswerId).ToList();

                foreach (int id in answerIDs) {
                    DeleteAnswer(id);
                }
            }

            UnitOfWork.Questions.DeleteObject(question);
            UnitOfWork.Commit();
        }

        public virtual void EditAnswer(AnswerModel answer) {
            Answer edit = UnitOfWork.Answers.Where(a => a.AnswerId == answer.AnswerId).SingleOrDefault();
            Util.CopyFromModel(answer, ref edit);
            UnitOfWork.Commit();
        }

        /// <summary>
        ///   Edits the question.
        /// </summary>
        /// <param name = "question">The question.</param>
        public virtual void EditQuestion(QuestionModel question) {
            Question edit = UnitOfWork.Questions.Where(q => q.QuestionId == question.QuestionId).SingleOrDefault();
            Util.CopyFromModel(question, ref edit);
            UnitOfWork.Commit();
        }

        /// <summary>
        ///   Gets all forms.
        /// </summary>
        /// <param name = "showAll">if set to <c>true</c> [show all].</param>
        /// <param name = "fullGraph">if set to <c>true</c> [full graph].</param>
        /// <returns></returns>
        public virtual ICollection<StudyFormModel> GetAllForms(bool showAll = false, bool fullGraph = false) {
            UnitOfWork.LazyLoadingOn = !fullGraph;
            IQueryable<Form> query;
            if (fullGraph) {
                query = showAll
                            ? UnitOfWork.Forms.Include("Questions").OrderBy(f => f.SortOrder)
                            : UnitOfWork.Forms.Where(forms => forms.Status == (int)StudyStatus.ActiveStatus).Include(
                                "Questions").OrderBy(f => f.SortOrder);
            } else {
                query = showAll
                            ? UnitOfWork.Forms.OrderBy(f => f.SortOrder)
                            : UnitOfWork.Forms.Where(forms => forms.Status == (int)StudyStatus.ActiveStatus).OrderBy(
                                f => f.SortOrder);
            }

            ICollection<Form> list = query.ToList();

            ICollection<StudyFormModel> formsList = list.Select(form => form.ToFormModel()).ToList();

            UnitOfWork.LazyLoadingOn = true;
            return formsList;
        }

        /// <summary>
        ///   Gets all forms.
        /// </summary>
        /// <param name = "formType">Type of the form.</param>
        /// <param name = "showAll">if set to <c>true</c> [show all].</param>
        /// <param name = "fullGraph">if set to <c>true</c> [full graph].</param>
        /// <returns></returns>
        public virtual ICollection<StudyFormModel> GetAllForms(StudyFormType formType, bool showAll = false,
                                                        bool fullGraph = false) {
            UnitOfWork.LazyLoadingOn = !fullGraph;
            IQueryable<Form> query;
            if (fullGraph) {
                query = showAll
                            ? UnitOfWork.Forms.Where(forms => forms.FormType == (int)formType).Include("Questions").
                                  OrderBy(f => f.SortOrder)
                            : UnitOfWork.Forms.Where(
                                forms =>
                                forms.Status == (int)StudyStatus.ActiveStatus && forms.FormType == (int)formType).
                                  Include("Questions").OrderBy(f => f.SortOrder);
            } else {
                query = showAll
                            ? UnitOfWork.Forms.Where(forms => forms.FormType == (int)formType).OrderBy(f => f.SortOrder)
                            : UnitOfWork.Forms.Where(
                                forms =>
                                forms.Status == (int)StudyStatus.ActiveStatus && forms.FormType == (int)formType).
                                  OrderBy(f => f.SortOrder);
            }

            ICollection<Form> list = query.ToList();

            ICollection<StudyFormModel> formsList = list.Select(form => form.ToFormModel()).ToList();

            UnitOfWork.LazyLoadingOn = true;
            return formsList;
        }

        public virtual AnswerModel GetAnswer(int answerId) {
            return UnitOfWork.Answers.Where(a => a.AnswerId == answerId).FirstOrDefault().ToAnswerModel();
        }

        public virtual ICollection<QuestionModel> GetChildQuestions(int parentQuestionId) {
            IQueryable<Question> query = UnitOfWork.
                Questions.
                Where(q => q.ParentQuestionId == parentQuestionId).
                Include("FieldDataType").
                Include("Answers").
                OrderBy(q => q.SortOrder);

            ICollection<QuestionModel> list = query.ToList().ToQuestionModelList();

            UnitOfWork.LazyLoadingOn = true;
            return list;
        }

        /// <summary>
        ///   Gets the form.
        /// </summary>
        /// <param name = "formId">The form id.</param>
        /// <param name = "fullGraph">if set to <c>true</c> [full graph].</param>
        /// <returns></returns>
        public virtual StudyFormModel GetForm(int formId, bool fullGraph = false) {
            UnitOfWork.LazyLoadingOn = !fullGraph;

            Form frm;
            if (fullGraph) {
                frm = (UnitOfWork.Forms.Where(form => form.FormId == formId).Include("Questions").FirstOrDefault());
                if (frm == null) return new StudyFormModel();
            } else {
                frm = (UnitOfWork.Forms.Where(form => form.FormId == formId).FirstOrDefault());
            }
            UnitOfWork.LazyLoadingOn = true;

            return frm.ToFormModel();
        }

        /// <summary>
        ///   Gets the form questions.
        ///   return only parent questions. HasChildren is set to true if any children question...
        /// </summary>
        /// <param name = "formId">The form id.</param>
        /// <param name = "fullGraph">if set to <c>true</c> [full graph].</param>
        /// <param name = "showChildren"></param>
        /// <returns></returns>
        public virtual ICollection<QuestionModel> GetFormQuestions(int formId, bool fullGraph = false, bool showChildren = true) {
            UnitOfWork.LazyLoadingOn = !fullGraph;
            var list = new Collection<QuestionModel>();
            var query = fullGraph
                        ? UnitOfWork.
                            Questions.
                            Where(q => q.FormId == formId).
                            Include("FieldDataType").
                            Include("Answers").
                            OrderBy(q => q.SortOrder)
                        : UnitOfWork.
                            Questions.
                            Where(q => q.FormId == formId).
                            Include("FieldDataType").
                            OrderBy(q => q.SortOrder);

            var questions = showChildren
                            ? query.ToList()
                            : query.Where(q => q.ParentQuestionId == null || q.ParentQuestionId == 0).
                                    ToList();
            foreach (var q in questions) {
                var qmodel = q.ToQuestionModel();
                //Check each question if it is parent question.
                //TODO: [SK] optimize this code to return in one query for all questions on the form.
                var q1 = q;
                if (UnitOfWork.Questions != null) {
                    var hasKids = UnitOfWork.Questions.Where(ques => ques.ParentQuestionId != q1.QuestionId);
                    qmodel.HasChildren = hasKids.Count() > 0;
                }
                list.Add(qmodel);
            }

            UnitOfWork.LazyLoadingOn = true;
            return list;
        }

        /// <summary>
        ///   Gets the question.
        /// </summary>
        /// <param name = "questionId">The question id.</param>
        /// <param name = "fullGraph">if set to <c>true</c> [full graph].</param>
        /// <returns></returns>
        public virtual QuestionModel GetQuestion(int questionId, bool fullGraph = true) {
            UnitOfWork.LazyLoadingOn = !fullGraph;

            IQueryable<Question> query = fullGraph
                                             ? UnitOfWork.Questions.Where(q => q.QuestionId == questionId).Include(
                                                 "Answers").OrderBy(q => q.SortOrder)
                                             : UnitOfWork.Questions.Where(q => q.QuestionId == questionId).OrderBy(
                                                 q => q.SortOrder);

            QuestionModel question = query.FirstOrDefault().ToQuestionModel();
            UnitOfWork.LazyLoadingOn = true;
            return question;
        }

        public virtual ICollection<AnswerModel> GetQuestionAnswers(int qustionId) {
            UnitOfWork.LazyLoadingOn = false;
            IQueryable<Answer> query = UnitOfWork.Answers.Where(a => a.QuestionId == qustionId).OrderBy(q => q.SortOrder);
            ICollection<AnswerModel> list = query.ToList().ToAnswerModelList();
            UnitOfWork.LazyLoadingOn = true;
            return list;
        }

        public virtual void UpdateForm(StudyFormModel form) {
            Form editForm = UnitOfWork.Forms.Where(frm => frm.FormId == form.FormId).SingleOrDefault();
            Util.CopyFromModel(form, ref editForm);

            UnitOfWork.Commit();
        }

        public virtual void ViewForm(StudyFormModel form, FormLayoutType layout) {
            throw new NotImplementedException();
        }

        #endregion
    }
}